package cn.edu.xmu.oomall.customer.dao;

import cn.edu.xmu.oomall.core.util.ReturnNo;
import cn.edu.xmu.oomall.core.util.ReturnObject;
import cn.edu.xmu.oomall.customer.mapper.CustomerPoMapper;
import cn.edu.xmu.oomall.customer.model.bo.Customer;
import cn.edu.xmu.oomall.customer.model.bo.MailSenderProperty;
import cn.edu.xmu.oomall.customer.model.po.CustomerPo;
import cn.edu.xmu.oomall.core.util.Common;
import cn.edu.xmu.oomall.customer.model.po.CustomerPoExample;
import cn.edu.xmu.oomall.customer.model.vo.NewPasswordVo;
import cn.edu.xmu.privilegegateway.annotation.util.JwtHelper;
import cn.edu.xmu.privilegegateway.annotation.util.RandomCaptcha;
import cn.edu.xmu.privilegegateway.annotation.util.RedisUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Repository;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

import static cn.edu.xmu.privilegegateway.annotation.util.Common.cloneVo;
import static cn.edu.xmu.privilegegateway.annotation.util.Common.setPoCreatedFields;

/**
 * @author: zhang yu
 * @date: 2021/12/8 18:00
 * @version: 1.0
*/
@Repository
public class CustomerDao {
    @Autowired
    CustomerPoMapper customerPoMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private JavaMailSender mailSender;
    /**
     * 验证码的redis key: cp_id
     */
    private final static String CAPTCHAKEY = "cp_%s";
    /*将用户信息存入redis来限制访问*/
    private final static String USERVISITLIMIT="limit_%s";
    /*限制访问时间*/
    @Value("${customer.resetpassword.sendCaptchaLimit}")
    private Long TIMElIMIT;

    @Value("${customer.login.multiply}")
    private Boolean CANMULTIPLYLOGIN;

    private String USERKEY="CUS_%d";

    //token过期时间：单位秒
    @Value("${customer.login.jwtExpire}")
    private Integer EXPIRETIME;
    @Value("${customer.resetpassword.captchaExpire}")
    private Integer CaptchaExpireTime;

    private static JwtHelper jwtHelper=new JwtHelper();

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MailSenderProperty property;


    public ReturnObject insertUser(Customer customer)
    {
        try
        {
            //唯一索引不能插入空
            //判断mobile是否存在
            CustomerPoExample example=new CustomerPoExample();
            CustomerPoExample.Criteria criteria=example.createCriteria();
            criteria.andMobileEqualTo(customer.getMobile());
            List<CustomerPo> mobliepos=customerPoMapper.selectByExample(example);
            if(mobliepos.size()>0)
            {
                return new ReturnObject(ReturnNo.CUSTOMER_MOBILEEXIST);
            }
            //判断email是否存在
            example.clear();
            CustomerPoExample.Criteria criteria1=example.createCriteria();
            criteria1.andEmailEqualTo(customer.getEmail());
            List<CustomerPo> emailpos=customerPoMapper.selectByExample(example);
            if(emailpos.size()>0)
            {
                return new ReturnObject(ReturnNo.CUSTOMER_EMAILEXIST);
            }
            //判断用户名是否存在
            example.clear();
            CustomerPoExample.Criteria criteria2=example.createCriteria();
            criteria2.andUserNameEqualTo(customer.getUserName());
            List<CustomerPo> usernamepos=customerPoMapper.selectByExample(example);
            if(usernamepos.size()>0)
            {
                return new ReturnObject(ReturnNo.CUSTOMER_NAMEEXIST);
            }
            CustomerPo po= cloneVo(customer,CustomerPo.class);
            po.setState((byte)0);
            po.setBeDeleted((byte) 0);
            po.setPoint(0L);
            customerPoMapper.insertSelective(po);
            return new ReturnObject(po);
        }
        catch (Exception e)
        {
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR);
        }
    }
    public ReturnObject selectUserById(Long userId)
    {
        try
        {
            CustomerPo po=customerPoMapper.selectByPrimaryKey(userId);
            if(po==null)
            {
                return new ReturnObject(ReturnNo.RESOURCE_ID_NOTEXIST);
            }
            return new ReturnObject(po);
        }catch (Exception e)
        {
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR);
        }
    }
    public ReturnObject selectAllUser(Customer customer,Integer page,Integer pageSize)
    {
        try {
            CustomerPoExample example = new CustomerPoExample();
            CustomerPoExample.Criteria criteria = example.createCriteria();
            if (customer.getUserName() != null) {
                criteria.andUserNameEqualTo(customer.getUserName());
            }
            if (customer.getEmail() != null) {
                criteria.andEmailEqualTo(customer.getEmail());
            }
            if (customer.getMobile() != null) {
                criteria.andMobileEqualTo(customer.getMobile());
            }
            PageHelper.startPage(page,pageSize);
            List<CustomerPo> customerPos = customerPoMapper.selectByExample(example);
            return new ReturnObject(customerPos);
        }catch (Exception e)
        {
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR);
        }
    }
    public ReturnObject updateUser(Customer customer)
    {
        try
        {
            CustomerPo po=cloneVo(customer,CustomerPo.class);
            po.setState(customer.getState());
            customerPoMapper.updateByPrimaryKeySelective(po);
            return new ReturnObject(po);
        }catch (Exception e)
        {
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR);
        }
    }
    public ReturnObject createCaptcha(Long userId)
    {
        try {
            //随机生成验证码
            String captcha = RandomCaptcha.getRandomString(6);
            while (redisUtil.hasKey(captcha))
            {
                captcha = RandomCaptcha.getRandomString(6);
            }
            String id = userId.toString();
            String key =  String.format(CAPTCHAKEY, captcha);
            redisUtil.set(key, id, CaptchaExpireTime);
            return new ReturnObject(captcha);
        } catch (Exception e) {
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR);
        }
    }
    public ReturnObject sendEmail(String message,String to)
    {
        try
        {
            //发送邮件(请在配置文件application.properties填写密钥)
            property.setText(String.format(property.getFormat(),message));
            SimpleMailMessage msg =new SimpleMailMessage();
            msg.setText(property.getText());
            msg.setFrom(property.getFrom());
            msg.setSentDate(new Date());
            msg.setSubject(property.getSubject());
            msg.setTo(to);
            mailSender.send(msg);
            return new ReturnObject(ReturnNo.OK);
        }catch (MailException e)
        {
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR);
        }
    }
    public ReturnObject verifyCaptcha(String captcha)
    {
        try {
            String key = String.format(CAPTCHAKEY, captcha);
            //通过验证码取出id
            if (!redisUtil.hasKey(key)) {
                //// TODO: 2021/12/24 邱明测试是这个 
                return new ReturnObject(ReturnNo.CUSTOMERID_NOTEXIST);
            }
            Long id = Long.valueOf((String) redisUtil.get( key));
            //删除验证码
            redisUtil.del(key);
            return new ReturnObject(id);
        }catch (Exception e)
        {
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR);
        }
    }
    //限制访问
    public ReturnObject LimitVisit(String name)
    {
        String key =String.format(USERVISITLIMIT,name);
        if(redisUtil.hasKey(key))
        {
            //这里的确不知道该返回什么
            return new ReturnObject(ReturnNo.STATENOTALLOW);
        }
        //随便存入一个
        redisUtil.set(key,0,TIMElIMIT);
        return new ReturnObject(ReturnNo.OK);
    }
    //生成token
    public ReturnObject createtoken(CustomerPo po)
    {
        try {

            String key = String.format(USERKEY, po.getId());
            if(!CANMULTIPLYLOGIN) {
                bantoken(po.getId());
            }
            //生成token
            //todo 不知道departid,和userlevel
            String userToken = jwtHelper.createToken(po.getId(), po.getUserName(), 1L, 1, EXPIRETIME);
            redisUtil.addSet(key,userToken,EXPIRETIME);
            return new ReturnObject(userToken);
        }catch (Exception e)
        {
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR);
        }
    }
    public void bantoken(Long id)
    {
        String key = String.format(USERKEY, id);
        Set<Serializable> set = redisUtil.getSet(key);
        String jwt = null;
        for (Serializable str : set) {
            /* 找出JWT */
            if ((str.toString()).length() > 8) {
                jwt = str.toString();
                banJwt(jwt);
            }
        }
        redisUtil.del(key);
    }
    /**
     * 来自权限模块，直接拖过来了
     * 用lua脚本重写该方法
     * @author Jianjian Chan
     * @date 2021/12/02
     */
    private void banJwt(String jwt) {
        String[] banSetName = {"BanJwt_0", "BanJwt_1"};
        String banIndexKey = "banIndex";
        String scriptPath = "scripts/ban-jwt.lua";

        DefaultRedisScript<Void> script = new DefaultRedisScript<>();

        script.setScriptSource(new ResourceScriptSource(new ClassPathResource(scriptPath)));
        script.setResultType(Void.class);

        List<String> keyList = new ArrayList<>(List.of(banSetName));
        keyList.add(banIndexKey);
        redisTemplate.execute(script, keyList, banSetName.length, jwt, EXPIRETIME);
    }
    public ReturnObject Logout(Long userId)
    {
        try {
            bantoken(userId);
            return new ReturnObject(ReturnNo.OK);
        }catch (Exception e)
        {
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR);
        }
    }
    //通过任意一个查询 允许进行或方式查询
    public ReturnObject selectUserByIndex(Customer customer)
    {
        try {
            CustomerPoExample example = new CustomerPoExample();
            CustomerPoExample.Criteria criteria = example.createCriteria();
            if (customer.getUserName() != null) {
                CustomerPoExample.Criteria criteria1 = example.createCriteria();
                criteria1.andUserNameEqualTo(customer.getUserName());
                example.or(criteria1);
            }
            if (customer.getEmail() != null) {
                CustomerPoExample.Criteria criteria2 = example.createCriteria();
                criteria2.andEmailEqualTo(customer.getEmail());
                example.or(criteria2);
            }
            if (customer.getMobile() != null) {
                CustomerPoExample.Criteria criteria3 = example.createCriteria();
                criteria3.andMobileEqualTo(customer.getMobile());
                example.or(criteria3);
            }
            List<CustomerPo> customerPos = null;
            customerPos=customerPoMapper.selectByExample(example);
            return new ReturnObject(customerPos);
        }catch (Exception e)
        {
            return new ReturnObject(ReturnNo.INTERNAL_SERVER_ERR);
        }
    }

    private Logger logger = LoggerFactory.getLogger(CustomerDao.class);

    public void addCustomerPoint(Long id, Long point) {
        try{
            CustomerPo customerPo = customerPoMapper.selectByPrimaryKey(id);
            customerPo.setPoint(customerPo.getPoint()+point);
            customerPoMapper.updateByPrimaryKeySelective(customerPo);
        }
        catch (Exception e) {
            logger.error(e.getMessage());
        }
    }
}
